/* Copyright (c) [2025] [zhi1208@126.com]
 * SPDX-License-Identifier: MIT
 */

/*********************
 *      INCLUDES
 *********************/
#include "hmi_pg_def.h"
#include "hmi_top_bar.h"
#include "hmi_config.h"
#include "hmi_bg.h"
#include "hmi_anim.h"
#include "hmi_list_item.h"
#include "hmi_tinyttf.h"
#include "hmi_subjects.h"
#include "hmi_hal.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void create();
static void del();
static void enter_anim(lv_anim_completed_cb_t cb, uint32_t delay);
static uint32_t exit_anim(lv_anim_completed_cb_t cb, hmi_pg_t *enter_pg);

static void atuo_sw_ob_cb(lv_observer_t *observer, lv_subject_t *subject);
static void date_time_item_click_cb(lv_event_t *e);

static lv_obj_t *date_set_up_panel_create(lv_obj_t *parent);
static void panel_cancel_click_cb(lv_event_t *e);
static void date_panel_confirm_click_cb(lv_event_t *e);
static void month_roller_cb(lv_event_t *e);
static int32_t calculate_day_of_week(int year, int month, int day);

static lv_obj_t *time_set_up_panel_create(lv_obj_t *parent);
static void time_panel_confirm_click_cb(lv_event_t *e);

static void panel_anim_in(lv_obj_t *panel);
static void panel_anim_out(lv_obj_t *panel);

/**********************
 *  STATIC VARIABLES
 **********************/
static hmi_pg_t dt = {
    .name = "时间和日期",
    .del_on_pop = 1,
    .bg_color = 0x95e8d7,
    .create = create,
    .del = del,
    .enter_anim = enter_anim,
    .exit_anim = exit_anim,
};

static lv_obj_t *date_info;
static lv_obj_t *time_info;

static lv_font_t *info_font;
static lv_font_t *panel_font;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/
hmi_pg_t *hmi_pg_dt()
{
    return &dt;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static void create()
{
    dt.bg_y = HMI_BG_NORMAL;

    dt.root = lv_obj_create(NULL);
    lv_obj_remove_style_all(dt.root);
    lv_obj_set_flex_flow(dt.root, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(dt.root, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
    lv_obj_set_style_pad_bottom(dt.root, 10, 0);
    lv_obj_remove_flag(dt.root, LV_OBJ_FLAG_SCROLLABLE);

    hmi_top_bar_create(dt.root, dt.name);

    lv_obj_t *cont = lv_obj_create(dt.root);
    lv_obj_set_width(cont, lv_pct(94));
    lv_obj_set_style_border_width(cont, 0, 0);
    lv_obj_set_flex_grow(cont, 1);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);

    lv_obj_t *date_set_up_panel = date_set_up_panel_create(dt.root);
    lv_obj_t *time_set_up_panel = time_set_up_panel_create(dt.root);

    lv_obj_t *auto_sw = lv_switch_create(cont);
    lv_obj_set_style_bg_color(auto_sw, lv_color_hex(dt.bg_color), LV_PART_INDICATOR | LV_STATE_CHECKED);
    lv_obj_bind_checked(auto_sw, &hmi_auto_dt_subj);

    lv_subject_t *year_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 0);
    lv_subject_t *month_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 1);
    lv_subject_t *day_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 2);
    lv_subject_t *hour_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 4);
    lv_subject_t *minute_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 5);

    int32_t year = lv_subject_get_int(year_subj);
    int32_t month = lv_subject_get_int(month_subj);
    int32_t day = lv_subject_get_int(day_subj);
    int32_t hour = lv_subject_get_int(hour_subj);
    int32_t minute = lv_subject_get_int(minute_subj);

    if (!info_font)
        info_font = hmi_tiny_ttf_create_static("def_font", 18, 0);

    date_info = lv_label_create(cont);
    lv_obj_set_style_text_font(date_info, info_font, 0);
    lv_obj_set_style_text_color(date_info, lv_palette_main(LV_PALETTE_GREY), 0);
    lv_label_set_text_fmt(date_info, "%ld-%ld-%ld", year, month, day);

    time_info = lv_label_create(cont);
    lv_obj_set_style_text_font(time_info, info_font, 0);
    lv_obj_set_style_text_color(time_info, lv_palette_main(LV_PALETTE_GREY), 0);
    lv_label_set_text_fmt(time_info, "%02ld:%02ld", hour, minute);

    hmi_list_item_cfg_t auto_dt_cfg = {.title = "自动时间", .info = auto_sw};
    hmi_list_item_create(cont, &auto_dt_cfg);

    hmi_list_item_cfg_t date_dt_cfg = {
        .title = "日期",
        .click_cb = date_time_item_click_cb,
        .click_cb_user_data = date_set_up_panel,
        .info = date_info};
    hmi_list_item_create(cont, &date_dt_cfg);

    hmi_list_item_cfg_t time_dt_cfg = {
        .title = "时间",
        .click_cb = date_time_item_click_cb,
        .click_cb_user_data = time_set_up_panel,
        .info = time_info};
    hmi_list_item_create(cont, &time_dt_cfg);

    lv_subject_add_observer_obj(&hmi_auto_dt_subj, atuo_sw_ob_cb, auto_sw, cont);
}

static void del()
{
    lv_obj_delete_async(dt.root);
    dt.root = NULL;

    if (info_font)
    {
        hmi_tiny_ttf_del(info_font);
        info_font = NULL;
    }

    if (panel_font)
    {
        hmi_tiny_ttf_del(panel_font);
        panel_font = NULL;
    }
}

static void enter_anim(lv_anim_completed_cb_t cb, uint32_t delay)
{
    hmi_enter_anim_default(dt.root, cb, delay);
}
static uint32_t exit_anim(lv_anim_completed_cb_t cb, hmi_pg_t *enter_pg)
{
    return hmi_exit_anim_default(dt.root, cb, enter_pg);
}

static void atuo_sw_ob_cb(lv_observer_t *observer, lv_subject_t *subject)
{
    lv_obj_t *parent = lv_observer_get_user_data(observer);
    int32_t child_cnt = lv_obj_get_child_cnt(parent);

    int32_t is_hidden = lv_subject_get_int(subject);

    for (int32_t i = 1; i < child_cnt; i++)
    {
        lv_obj_t *child = lv_obj_get_child(parent, i);
        if (is_hidden)
            lv_obj_add_flag(child, LV_OBJ_FLAG_HIDDEN);
        else
            lv_obj_remove_flag(child, LV_OBJ_FLAG_HIDDEN);
    }
}
static void date_time_item_click_cb(lv_event_t *e)
{
    lv_obj_t *obj = lv_event_get_user_data(e);

    panel_anim_in(obj);
}

static lv_obj_t *date_set_up_panel_create(lv_obj_t *parent)
{
    lv_obj_t *panel = lv_obj_create(parent);
    lv_obj_set_size(panel, lv_pct(80), LV_SIZE_CONTENT);
    lv_obj_set_style_pad_row(panel, 30, 0);
    lv_obj_set_style_border_width(panel, 0, 0);
    lv_obj_set_style_shadow_color(panel, lv_palette_main(LV_PALETTE_GREY), 0);
    lv_obj_set_style_shadow_width(panel, 15, 0);
    lv_obj_align(panel, LV_ALIGN_CENTER, 0, LV_VER_RES);
    lv_obj_add_flag(panel, LV_OBJ_FLAG_IGNORE_LAYOUT);

    static int32_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static int32_t row_dsc[] = {LV_GRID_FR(4), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(panel, col_dsc, row_dsc);

    if (!panel_font)
        panel_font = hmi_tiny_ttf_create_static("def_font", 20, 0);

    lv_subject_t *year_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 0);
    lv_subject_t *month_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 1);
    lv_subject_t *day_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 2);
    int32_t year = lv_subject_get_int(year_subj);
    int32_t month = lv_subject_get_int(month_subj);
    int32_t day = lv_subject_get_int(day_subj);

    lv_obj_t *year_roller = lv_roller_create(panel);
    lv_obj_set_width(year_roller, lv_pct(30));
    lv_obj_set_style_bg_color(year_roller, lv_color_hex(dt.bg_color), LV_PART_SELECTED);
    lv_roller_set_options(
        year_roller,
        "2000\n2001\n2002\n2003\n2004\n2005\n2006\n2007\n2008\n2009\n2010\n2011\n2012\n2013\n2014\n2015\n2016\n2017\n2018\n2019\n2020\n2021\n2022\n2023\n2024\n"
        "2025\n2026\n2027\n2028\n2029\n2030\n2031\n2032\n2033\n2034\n2035\n2036\n2037\n2038\n2039\n2040\n2041\n2042\n2043\n2044\n2045\n2046\n2047\n2048\n2049\n"
        "2050\n2051\n2052\n2053\n2054\n2055\n2056\n2057\n2058\n2059\n2060\n2061\n2062\n2063\n2064\n2065\n2066\n2067\n2068\n2069\n2070\n2071\n2072\n2073\n2074\n"
        "2075\n2076\n2077\n2078\n2079\n2080\n2081\n2082\n2083\n2084\n2085\n2086\n2087\n2088\n2089\n2090\n2091\n2092\n2093\n2094\n2095\n2096\n2097\n2098\n2099",
        LV_ROLLER_MODE_INFINITE);
    lv_roller_set_selected(year_roller, year - 2000, LV_ANIM_OFF);
    lv_roller_set_visible_row_count(year_roller, 5);

    lv_obj_t *month_roller = lv_roller_create(panel);
    lv_obj_set_width(month_roller, lv_pct(30));
    lv_obj_set_style_bg_color(month_roller, lv_color_hex(dt.bg_color), LV_PART_SELECTED);
    lv_roller_set_options(
        month_roller,
        "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12",
        LV_ROLLER_MODE_INFINITE);
    lv_roller_set_selected(month_roller, month - 1, LV_ANIM_OFF);
    lv_roller_set_visible_row_count(month_roller, 5);

    lv_obj_t *day_roller = lv_roller_create(panel);
    lv_obj_set_width(day_roller, lv_pct(30));
    lv_obj_set_style_bg_color(day_roller, lv_color_hex(dt.bg_color), LV_PART_SELECTED);
    lv_roller_set_options(
        day_roller,
        "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31",
        LV_ROLLER_MODE_INFINITE);
    lv_roller_set_selected(day_roller, day - 1, LV_ANIM_OFF);
    lv_roller_set_visible_row_count(day_roller, 5);

    static lv_obj_t *roller_list[3];
    roller_list[0] = year_roller;
    roller_list[1] = month_roller;
    roller_list[2] = day_roller;

    lv_obj_add_event_cb(month_roller, month_roller_cb, LV_EVENT_VALUE_CHANGED, &roller_list);

    lv_obj_t *cancel_btn = lv_button_create(panel);
    lv_obj_set_size(cancel_btn, lv_pct(30), 40);
    lv_obj_set_style_radius(cancel_btn, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_bg_color(cancel_btn, lv_color_hex(dt.bg_color), 0);
    lv_obj_add_flag(cancel_btn, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
    lv_obj_add_event_cb(cancel_btn, panel_cancel_click_cb, LV_EVENT_CLICKED, NULL);

    lv_obj_t *cancel_label = lv_label_create(cancel_btn);
    lv_obj_center(cancel_label);
    lv_obj_set_style_text_font(cancel_label, panel_font, 0);
    lv_label_set_text(cancel_label, "取消");

    lv_obj_t *confirm_btn = lv_button_create(panel);
    lv_obj_set_size(confirm_btn, lv_pct(30), 40);
    lv_obj_set_style_bg_color(confirm_btn, lv_color_hex(dt.bg_color), 0);
    lv_obj_set_style_radius(confirm_btn, LV_RADIUS_CIRCLE, 0);
    lv_obj_add_event_cb(confirm_btn, date_panel_confirm_click_cb, LV_EVENT_CLICKED, &roller_list);

    lv_obj_t *confirm_label = lv_label_create(confirm_btn);
    lv_obj_center(confirm_label);
    lv_obj_set_style_text_font(confirm_label, panel_font, 0);
    lv_label_set_text(confirm_label, "确认");

    lv_obj_set_grid_cell(year_roller, LV_GRID_ALIGN_CENTER, 0, 2, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(month_roller, LV_GRID_ALIGN_CENTER, 2, 2, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(day_roller, LV_GRID_ALIGN_CENTER, 4, 2, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(cancel_btn, LV_GRID_ALIGN_CENTER, 0, 3, LV_GRID_ALIGN_CENTER, 1, 1);
    lv_obj_set_grid_cell(confirm_btn, LV_GRID_ALIGN_CENTER, 3, 3, LV_GRID_ALIGN_CENTER, 1, 1);

    return panel;
}

static void panel_cancel_click_cb(lv_event_t *e)
{
    lv_obj_t *obj = lv_event_get_target_obj(e);
    lv_obj_t *parent = lv_obj_get_parent(obj);
    panel_anim_out(parent);
}

static void date_panel_confirm_click_cb(lv_event_t *e)
{
    lv_obj_t *obj = lv_event_get_target_obj(e);
    lv_obj_t *parent = lv_obj_get_parent(obj);
    panel_anim_out(parent);

    lv_obj_t **roller_list = lv_event_get_user_data(e);

    int32_t year = lv_roller_get_selected(roller_list[0]) + 2000;
    int32_t month = lv_roller_get_selected(roller_list[1]) + 1;
    int32_t day = lv_roller_get_selected(roller_list[2]) + 1;
    int32_t week_day = calculate_day_of_week(year, month, day);

    struct tm *timeinfo = hmi_hal_dt_get();
    if (timeinfo)
    {
        timeinfo->tm_year = year - 1900;
        timeinfo->tm_mon = month - 1;
        timeinfo->tm_mday = day;
        timeinfo->tm_wday = week_day;
        hmi_hal_dt_set(timeinfo);
        lv_free(timeinfo);
    }

    lv_subject_t *year_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 0);
    lv_subject_t *month_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 1);
    lv_subject_t *day_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 2);
    lv_subject_t *weekday_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 3);

    lv_subject_set_int(year_subj, year);
    lv_subject_set_int(month_subj, month);
    lv_subject_set_int(day_subj, day);
    lv_subject_set_int(weekday_subj, week_day);

    lv_label_set_text_fmt(date_info, "%ld-%ld-%ld", year, month, day);
}

static void month_roller_cb(lv_event_t *e)
{
    lv_obj_t **roller_list = lv_event_get_user_data(e);

    int32_t year = lv_roller_get_selected(roller_list[0]) + 2000;
    int32_t month = lv_roller_get_selected(roller_list[1]) + 1;

    switch (month)
    {
    case 1:
    case 3:
    case 5:
    case 7:
    case 8:
    case 10:
    case 12:
        lv_roller_set_options(
            roller_list[2],
            "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31",
            LV_ROLLER_MODE_INFINITE);
        break;
    case 4:
    case 6:
    case 9:
    case 11:
        lv_roller_set_options(
            roller_list[2],
            "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30",
            LV_ROLLER_MODE_INFINITE);
        break;
    case 2:
        if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
            lv_roller_set_options(
                roller_list[2],
                "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29",
                LV_ROLLER_MODE_INFINITE);
        else
            lv_roller_set_options(
                roller_list[2],
                "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28",
                LV_ROLLER_MODE_INFINITE);
        break;
    default:
        break;
    }
}

static int32_t calculate_day_of_week(int year, int month, int day)
{
    if (month < 3)
    {
        month += 12;
        year -= 1;
    }

    int century = year / 100;
    int year_of_century = year % 100;

    int h = (day + 13 * (month + 1) / 5 + year_of_century + year_of_century / 4 + century / 4 - 2 * century) % 7;

    if (h < 0)
        h += 7;

    return (h + 6) % 7;
}

static lv_obj_t *time_set_up_panel_create(lv_obj_t *parent)
{
    lv_obj_t *panel = lv_obj_create(parent);
    lv_obj_set_size(panel, lv_pct(80), LV_SIZE_CONTENT);
    lv_obj_set_style_pad_row(panel, 30, 0);
    lv_obj_set_style_border_width(panel, 0, 0);
    lv_obj_set_style_shadow_color(panel, lv_palette_main(LV_PALETTE_GREY), 0);
    lv_obj_set_style_shadow_width(panel, 15, 0);
    lv_obj_align(panel, LV_ALIGN_CENTER, 0, LV_VER_RES);
    lv_obj_add_flag(panel, LV_OBJ_FLAG_IGNORE_LAYOUT);

    static int32_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    static int32_t row_dsc[] = {LV_GRID_FR(4), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
    lv_obj_set_grid_dsc_array(panel, col_dsc, row_dsc);

    if (!panel_font)
        panel_font = hmi_tiny_ttf_create_static("def_font", 20, 0);

    lv_subject_t *hour_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 4);
    lv_subject_t *minute_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 5);
    int32_t hour = lv_subject_get_int(hour_subj);
    int32_t minute = lv_subject_get_int(minute_subj);

    lv_obj_t *hour_roller = lv_roller_create(panel);
    lv_obj_set_width(hour_roller, lv_pct(40));
    lv_obj_set_style_bg_color(hour_roller, lv_color_hex(dt.bg_color), LV_PART_SELECTED);
    lv_roller_set_options(
        hour_roller,
        "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23",
        LV_ROLLER_MODE_INFINITE);
    lv_roller_set_selected(hour_roller, hour, LV_ANIM_OFF);
    lv_roller_set_visible_row_count(hour_roller, 5);

    lv_obj_t *minute_roller = lv_roller_create(panel);
    lv_obj_set_width(minute_roller, lv_pct(40));
    lv_obj_set_style_bg_color(minute_roller, lv_color_hex(dt.bg_color), LV_PART_SELECTED);
    lv_roller_set_options(
        minute_roller,
        "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n"
        "20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30\n31\n32\n33\n34\n35\n36\n37\n38\n39\n"
        "40\n41\n42\n43\n44\n45\n46\n47\n48\n49\n50\n51\n52\n53\n54\n55\n56\n57\n58\n59",
        LV_ROLLER_MODE_INFINITE);
    lv_roller_set_selected(minute_roller, minute, LV_ANIM_OFF);
    lv_roller_set_visible_row_count(minute_roller, 5);

    static lv_obj_t *roller_list[2];
    roller_list[0] = hour_roller;
    roller_list[1] = minute_roller;

    lv_obj_t *cancel_btn = lv_button_create(panel);
    lv_obj_set_size(cancel_btn, lv_pct(30), 40);
    lv_obj_set_style_radius(cancel_btn, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_bg_color(cancel_btn, lv_color_hex(dt.bg_color), 0);
    lv_obj_add_flag(cancel_btn, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
    lv_obj_add_event_cb(cancel_btn, panel_cancel_click_cb, LV_EVENT_CLICKED, NULL);

    lv_obj_t *cancel_label = lv_label_create(cancel_btn);
    lv_obj_center(cancel_label);
    lv_obj_set_style_text_font(cancel_label, panel_font, 0);
    lv_label_set_text(cancel_label, "取消");

    lv_obj_t *confirm_btn = lv_button_create(panel);
    lv_obj_set_size(confirm_btn, lv_pct(30), 40);
    lv_obj_set_style_radius(confirm_btn, LV_RADIUS_CIRCLE, 0);
    lv_obj_set_style_bg_color(confirm_btn, lv_color_hex(dt.bg_color), 0);
    lv_obj_add_event_cb(confirm_btn, time_panel_confirm_click_cb, LV_EVENT_CLICKED, &roller_list);

    lv_obj_t *confirm_label = lv_label_create(confirm_btn);
    lv_obj_center(confirm_label);
    lv_obj_set_style_text_font(confirm_label, panel_font, 0);
    lv_label_set_text(confirm_label, "确认");

    lv_obj_set_grid_cell(hour_roller, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(minute_roller, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1);
    lv_obj_set_grid_cell(cancel_btn, LV_GRID_ALIGN_CENTER, 0, 1, LV_GRID_ALIGN_CENTER, 1, 1);
    lv_obj_set_grid_cell(confirm_btn, LV_GRID_ALIGN_CENTER, 1, 1, LV_GRID_ALIGN_CENTER, 1, 1);

    return panel;
}

static void time_panel_confirm_click_cb(lv_event_t *e)
{
    lv_obj_t *obj = lv_event_get_target_obj(e);
    lv_obj_t *parent = lv_obj_get_parent(obj);
    panel_anim_out(parent);

    lv_obj_t **roller_list = lv_event_get_user_data(e);

    int32_t hour = lv_roller_get_selected(roller_list[0]);
    int32_t minute = lv_roller_get_selected(roller_list[1]);

    struct tm *timeinfo = hmi_hal_dt_get();
    if (timeinfo)
    {
        timeinfo->tm_hour = hour;
        timeinfo->tm_min = minute;
        hmi_hal_dt_set(timeinfo);
        lv_free(timeinfo);
    }

    lv_subject_t *hour_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 4);
    lv_subject_t *minute_subj = lv_subject_get_group_element(&hmi_dt_subj_group, 5);
    lv_subject_set_int(hour_subj, hour);
    lv_subject_set_int(minute_subj, minute);

    lv_label_set_text_fmt(time_info, "%02ld:%02ld", hour, minute);
}

static void panel_anim_in(lv_obj_t *panel)
{
    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_var(&a, panel);
    lv_anim_set_duration(&a, HMI_ANIM_TIME * 2);
    lv_anim_set_delay(&a, 0);
    lv_anim_set_exec_cb(&a, hmi_anim_translate_y_exec_cb);
    lv_anim_set_values(&a, 0, -LV_VER_RES);
    lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
    lv_anim_start(&a);

    lv_obj_fade_in(panel, HMI_ANIM_TIME * 2 + 50, 0);
}

static void panel_anim_out(lv_obj_t *panel)
{
    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_var(&a, panel);
    lv_anim_set_duration(&a, HMI_ANIM_TIME * 2);
    lv_anim_set_delay(&a, 0);
    lv_anim_set_exec_cb(&a, hmi_anim_translate_y_exec_cb);
    lv_anim_set_values(&a, -LV_VER_RES, 0);
    lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
    lv_anim_start(&a);

    lv_obj_fade_out(panel, HMI_ANIM_TIME * 2 + 50, 0);
}
